home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / bin / jockey-gtk < prev    next >
Text File  |  2009-10-25  |  15KB  |  395 lines

  1. #!/usr/bin/python
  2.  
  3. # (c) 2007 Canonical Ltd.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  
  19. '''GTK user interface implementation.'''
  20.  
  21. import sys, os.path, os
  22.  
  23. import glib, gtk, gobject, pynotify
  24.  
  25. import jockey.ui
  26. from jockey.oslib import OSLib
  27.  
  28. class GtkUI(jockey.ui.AbstractUI):
  29.     '''GTK user interface implementation.'''
  30.  
  31.     #
  32.     # Implementation of required AbstractUI methods
  33.     # 
  34.  
  35.     def convert_keybindings(self, str):
  36.         '''Keyboard accelerator aware gettext() wrapper.
  37.         
  38.         This optionally converts keyboard accelerators to the appropriate
  39.         format for the frontend.
  40.  
  41.         A double underscore ('__') is converted to a real '_'.
  42.         '''
  43.         # nothing to do for GTK
  44.         return str
  45.  
  46.     def ui_init(self):
  47.         '''Initialize UI.'''
  48.  
  49.         # load UI
  50.         self.widgets = gtk.Builder()
  51.         ui_path = '/usr/share/jockey/jockey-gtk.ui'
  52.         if not os.path.exists(ui_path):
  53.             ui_path = os.path.join(os.path.dirname(__file__), 'jockey-gtk.ui')
  54.         self.widgets.add_from_file(ui_path)
  55.         self.widgets.connect_signals(self)
  56.  
  57.         self.w('label_license_label').set_label(self.string_license_label)
  58.         self.w('linkbutton_licensetext').set_label('(%s)' % self.string_details)
  59.         self.w('dialog_licensetext').set_title(self.string_license_dialog_title)
  60.  
  61.     def ui_show_main(self):
  62.         '''Show main window.'''
  63.  
  64.         # check $DISPLAY validity, etc.
  65.         try:
  66.             gtk.init_check()
  67.         except RuntimeError:
  68.             self.error_msg('Could not initialize GTK: invalid $DISPLAY?')
  69.             sys.exit(1)
  70.  
  71.         self.treeview = self.w('treeview_drivers')
  72.  
  73.         self.w('dialog_manager').set_title(self.main_window_title())
  74.  
  75.         # initialize handler treeview (but only do it once)
  76.         if not self.treeview.get_columns():
  77.             self.treeview.set_headers_visible(False)
  78.  
  79.             # icon
  80.             pixbuf_renderer = gtk.CellRendererPixbuf()
  81.             col_icon = gtk.TreeViewColumn()
  82.             col_icon.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
  83.             col_icon.set_expand(False)
  84.             col_icon.pack_start(pixbuf_renderer, False)
  85.             col_icon.set_attributes(pixbuf_renderer, pixbuf=1)
  86.             self.treeview.append_column(col_icon)
  87.  
  88.             # name
  89.             text_renderer = gtk.CellRendererText()
  90.             col_name = gtk.TreeViewColumn()
  91.             col_name.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
  92.             col_name.set_expand(True)
  93.             col_name.pack_start(text_renderer, True)
  94.             col_name.set_attributes(text_renderer, text=2)
  95.             self.treeview.append_column(col_name)
  96.  
  97.         self.treeview.grab_focus()
  98.         self.update_driver_info_ui(None)
  99.         self.update_tree_model()
  100.  
  101.         # show help button?
  102.         if not OSLib.inst.ui_help_available(self):
  103.             self.w('button_help').hide()
  104.             # EDGE is the default, but that centers if there is just one button
  105.             self.w('button_box_main').set_layout(gtk.BUTTONBOX_END)
  106.  
  107.         # default height of details scrollwindow is too small
  108.         self.w('dialog_manager').set_default_size(-1, 550)
  109.  
  110.         # default vpane size
  111.         if len(self.get_displayed_handlers()) < 2:
  112.             self.w('vpaned1').set_position(36)
  113.         elif len(self.get_displayed_handlers()) < 4:
  114.             self.w('vpaned1').set_position(72)
  115.         else:
  116.             self.w('vpaned1').set_position(100)
  117.  
  118.         # lift the curtain
  119.         self.w('dialog_manager').show()
  120.  
  121.     def ui_main_loop(self):
  122.         '''Main loop for the user interface.
  123.         
  124.         This should return if the user wants to quit the program, and return
  125.         the exit code.
  126.         '''
  127.         gtk.main()
  128.         self.w('dialog_manager').hide()
  129.  
  130.     def error_message(self, title, text):
  131.         '''Present an error message box.'''
  132.  
  133.         self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,
  134.             buttons=gtk.BUTTONS_CLOSE, message_format=text)
  135.         if title:
  136.             self.msgbox.set_title(title)
  137.         self.msgbox.run()
  138.         self.msgbox.destroy()
  139.         self.msgbox = None
  140.  
  141.     def confirm_action(self, title, text, subtext=None, action=None):
  142.         '''Present a confirmation dialog.
  143.  
  144.         If action is given, it is used as button label instead of the default
  145.         'OK'.  Return True if the user confirms, False otherwise.
  146.         '''
  147.         if action:
  148.             self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
  149.                 message_format=text)
  150.             self.msgbox.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
  151.             self.msgbox.add_button(action, gtk.RESPONSE_OK).set_image(
  152.                 gtk.image_new_from_stock(gtk.STOCK_APPLY,
  153.                 gtk.ICON_SIZE_BUTTON))
  154.         else:
  155.             self.msgbox = gtk.MessageDialog(type=gtk.MESSAGE_QUESTION,
  156.                 message_format=text, buttons=gtk.BUTTONS_OK_CANCEL)
  157.         if subtext:
  158.             self.msgbox.format_secondary_text(subtext)
  159.         if title:
  160.             self.msgbox.set_title(title)
  161.         ret = self.msgbox.run()
  162.         self.msgbox.destroy()
  163.         self.msgbox = None
  164.         return ret == gtk.RESPONSE_OK
  165.  
  166.     def ui_notification(self, title, text):
  167.         '''Present a notification popup.
  168.  
  169.         This should preferably create a tray icon. Clicking on the tray icon or
  170.         notification should run the GUI.
  171.         '''
  172.         pynotify.init('jockey')
  173.  
  174.         trayicon = gtk.status_icon_new_from_icon_name('jockey')
  175.         trayicon.connect('activate', lambda widget: self.ui_show_main())
  176.         trayicon.set_visible(False)
  177.         trayicon.set_tooltip(title)
  178.         trayicon.set_visible(True)
  179.  
  180.         self.ui_idle()
  181.  
  182.         # creating the notification immediately causes the bubble to point into
  183.         # the void; icon needs to settle first
  184.         gobject.timeout_add (500, self.show_notification, 
  185.             (title, text, trayicon))
  186.  
  187.     def show_notification(self, data):
  188.         (title, text, trayicon) = data
  189.         notify = pynotify.Notification(title, text, 'jockey')
  190.         notify.set_urgency(pynotify.URGENCY_NORMAL)
  191.         notify.attach_to_status_icon(trayicon)
  192.         notify.set_timeout(10000)
  193.         notify.show()
  194.  
  195.     def ui_idle(self):
  196.         '''Process pending UI events and return.
  197.  
  198.         This is called while waiting for external processes such as package
  199.         installers.
  200.         '''
  201.         while gtk.events_pending():
  202.             gtk.main_iteration(False)
  203.  
  204.     def ui_progress_start(self, title, description, total):
  205.         '''Create a progress dialog.'''
  206.  
  207.         self.w('progress').set_fraction(0)
  208.         self.w('label_description').set_label(description)
  209.         self.w('window_progress').set_title(title)
  210.         self.w('window_progress').set_transient_for(self.w('dialog_manager'))
  211.         self.w('window_progress').show()
  212.         self.cancel_progress = False
  213.  
  214.     def ui_progress_update(self, current, total):
  215.         '''Update status of current progress dialog.
  216.         
  217.         current/total specify the number of steps done and total steps to
  218.         do, or -1 if it cannot be determined. In this case the dialog should
  219.         display an indeterminated progress bar (bouncing back and forth).
  220.  
  221.         This should return True to cancel, and False otherwise.
  222.         '''
  223.         if current < 0 or total < 0:
  224.             self.w('progress').set_pulse_step(0.1)
  225.             self.w('progress').pulse()
  226.         else:
  227.             self.w('progress').set_fraction(float(current)/total)
  228.         return self.cancel_progress
  229.  
  230.     def ui_progress_finish(self):
  231.         '''Close the current progress dialog.'''
  232.  
  233.         self.w('window_progress').hide()
  234.  
  235.     #
  236.     # helper functions
  237.     #
  238.  
  239.     def w(self, widget):
  240.         '''Shortcut for getting an UI widget from GtkBuilder.'''
  241.  
  242.         return self.widgets.get_object(widget)
  243.  
  244.     def update_tree_model(self):
  245.         '''Update treeview to current set of handlers and their states.'''
  246.  
  247.         # handler ID, icon, name
  248.         self.model = gtk.ListStore(gobject.TYPE_STRING, gtk.gdk.Pixbuf, gobject.TYPE_STRING)
  249.  
  250.         theme = gtk.icon_theme_get_default()
  251.  
  252.         cur_path = self.treeview.get_cursor()[0]
  253.  
  254.         for h_id in self.get_displayed_handlers():
  255.             info = self.get_ui_driver_info(h_id)
  256.             if info['needs_reboot']:
  257.                 pixbuf = theme.load_icon(gtk.STOCK_REFRESH, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
  258.             elif info['enabled']:
  259.                 try:
  260.                     pixbuf = theme.load_icon('jockey-enabled', 16, 0)
  261.                 except glib.GError:
  262.                     pixbuf = theme.load_icon(gtk.STOCK_YES, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
  263.             else:
  264.                 try:
  265.                     pixbuf = theme.load_icon('jockey-disabled', 16, 0)
  266.                 except glib.GError:
  267.                     pixbuf = theme.load_icon(gtk.STOCK_NO, 16, gtk.ICON_LOOKUP_USE_BUILTIN)
  268.             iter = self.model.append([h_id, pixbuf, info['name']])
  269.             if not cur_path:
  270.                 cur_path = self.model.get_path(iter)
  271.  
  272.         self.treeview.set_model(self.model)
  273.         if cur_path:
  274.             self.treeview.set_cursor(cur_path)
  275.         self.treeview.expand_all()
  276.  
  277.         self.w('label_heading').set_label('<span weight="bold">%s</span>\n\n%s' %
  278.             self.main_window_text())
  279.  
  280.     def update_driver_info_ui(self, handler_id):
  281.         '''Update UI elements which show the driver details.
  282.  
  283.         If handler_id is None, then no driver is selected, no information
  284.         shown, and the appropriate controls are disabled.
  285.         '''
  286.         info = self.get_ui_driver_info(handler_id)
  287.  
  288.         self.w('label_drivername').set_label('<b>%s</b>' % info['name'])
  289.         self.current_driver_name = info['name']
  290.         self.w('textview_description').get_buffer().set_text(info['description'])
  291.         if info['certified'] == None:
  292.             self.w('image_certification').hide()
  293.         elif info['certified']:
  294.             self.w('image_certification').show()
  295.             self.w('image_certification').set_from_icon_name('jockey-certified', gtk.ICON_SIZE_BUTTON)
  296.         else:
  297.             self.w('image_certification').show()
  298.             self.w('image_certification').set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_BUTTON)
  299.         self.w('label_certification').set_label(info['certification_label'])
  300.         if info['free'] == None:
  301.             self.w('image_license').hide()
  302.             self.w('label_license_label').hide()
  303.         elif info['free']:
  304.             self.w('image_license').show()
  305.             self.w('label_license_label').show()
  306.             self.w('image_license').set_from_icon_name('jockey-free', gtk.ICON_SIZE_BUTTON)
  307.         else:
  308.             self.w('image_license').show()
  309.             self.w('label_license_label').show()
  310.             self.w('image_license').set_from_icon_name('jockey-proprietary',
  311.                 gtk.ICON_SIZE_BUTTON)
  312.         self.w('label_license').set_label(info['license_label'])
  313.         if info['license_text']:
  314.             self.w('linkbutton_licensetext').show()
  315.             self.current_license_text = info['license_text']
  316.         else:
  317.             self.w('linkbutton_licensetext').hide()
  318.             self.current_license_text = None
  319.         if info['enabled'] == None:
  320.             self.w('image_enabled').hide()
  321.         elif info['needs_reboot']:
  322.             self.w('image_enabled').show()
  323.             self.w('image_enabled').set_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_BUTTON)
  324.         elif info['enabled']:
  325.             self.w('image_enabled').show()
  326.             self.w('image_enabled').set_from_icon_name('jockey-enabled', gtk.ICON_SIZE_BUTTON)
  327.         else:
  328.             self.w('image_enabled').show()
  329.             self.w('image_enabled').set_from_icon_name('jockey-disabled', gtk.ICON_SIZE_BUTTON)
  330.         self.w('label_status').set_label(info['status_label'])
  331.  
  332.         if not info['button_toggle_label']:
  333.             self.w('button_toggle').set_label(self.string_button_enable)
  334.             self.w('button_toggle').set_sensitive(False)
  335.         else:
  336.             self.w('button_toggle').set_sensitive(True)
  337.             self.w('button_toggle').set_label(info['button_toggle_label'])
  338.             if info['enabled']:
  339.                 self.w('button_toggle').set_image(
  340.                     gtk.image_new_from_icon_name('jockey-disabled', gtk.ICON_SIZE_BUTTON))
  341.             else:
  342.                 self.w('button_toggle').set_image(
  343.                     gtk.image_new_from_icon_name('jockey-enabled', gtk.ICON_SIZE_BUTTON))
  344.  
  345.     #
  346.     # event callbacks
  347.     #
  348.  
  349.     def on_quit(self, *args):
  350.         gtk.main_quit()
  351.         return True
  352.  
  353.     def on_button_help_clicked(self, widget):
  354.         OSLib.inst.ui_help(self)
  355.         return True
  356.  
  357.     def on_button_toggle_clicked(self, widget):
  358.         self.treeview.set_sensitive(False)
  359.         if self.set_handler_enable(self.model[self.treeview.get_cursor()[0]][0],
  360.             'toggle', False):
  361.             self.update_tree_model()
  362.         self.treeview.set_sensitive(True)
  363.         return True
  364.  
  365.     def on_treeview_drivers_cursor_changed(self, widget):
  366.         path = widget.get_cursor()[0]
  367.         if path:
  368.             h_id = self.model[path][0]
  369.         else:
  370.             h_id = None
  371.         self.update_driver_info_ui(h_id)
  372.         return True
  373.  
  374.     def on_progress_cancel(self, *args):
  375.         self.cancel_progress = True
  376.         return True
  377.  
  378.     def on_linkbutton_licensetext_clicked(self, widget):
  379.         self.w('label_license_drivername').set_label('<b>%s</b>' %
  380.             self.current_driver_name)
  381.         self.w('textview_license_text').get_buffer().set_text(self.current_license_text)
  382.         self.w('dialog_licensetext').set_default_size(600, 480)
  383.         self.w('dialog_licensetext').run()
  384.         self.w('dialog_licensetext').hide()
  385.         return True
  386.  
  387. if __name__ == '__main__':
  388.     if not os.environ.get('DISPLAY'):
  389.         print >> sys.stderr, 'This program needs a running X session. Please use jockey-text for a command line version of Jockey.'
  390.         sys.exit(1)
  391.  
  392.     OSLib.inst = OSLib(client_only=True)
  393.     u = GtkUI()
  394.     sys.exit(u.run())
  395.